package org.yagnus.scalasupport.iterates;
/**
 * This class implements an iterator wrapper that allows us to break out of a for
 * loops <i>without the use of exceptions</i> and with specified behavior with regards
 * to the underlying iterator's "next" after the break;
 *
 * Note this implementation does not use throwables as the other implementations do.
 *
 * <code>
 * val itr = (0 until 10).iterator ;//some iterator
 * var processed = -1;
 * for{
 * 	   x<-makeBreakable(itr)
 *     if x.breakAfter{ x.value == 4}
 *    }{
 * 	   //do stuff with x;
 *     processed = x;
 * 	   println(x);
 * }
 * assert(processed=4);
 * assert(itr.next=5);
 * </code>
 *
 * This code will loop through that pre-existing iterator as much as possible but will stop after the condition inside
 * breakAfter has become true. (we can't really do breakWhen because when we know the current value of x, we'd have done
 * an iterator.next on the underlying iterator and can't push it back in.) There are two other ways to express this:
 *
 *    if x.breakAfter{ x.value >= 4}
 *
 * The frowning face operator:
 *
 *    if { x.value >= 4} >-: x
 *
 *  and the bleeped-for-letter-word-operator:
 *
 *    if { x.value >= 4} *&^: x
 *
 *  and the positive version:
 *
 *     if( x.value <=4) x.keepGoing;
 *
 *  Additionally x.value can be replaced with _
 *
 *     if { _ <= 4) stop
 *
 * @author hc.busy
 *
 */
final object BreakableIterator {

  trait BreakableIteratedValue[A] {
    def get: A;
    final def value = get;
    final def apply = get;
    def break: Unit;
    def broken: Boolean;

    /**
     * ******************************************
     *          * Break after the condition has reached. This leaves the underlying iterator consistent
     * ******************************************
     */
    final def breakAfter(f: ⇒ Boolean): Boolean = {
      if (broken || f) break;
      true; //never break current
    }

    final def breakAfter(f: A ⇒ Boolean): Boolean = {
      if (broken || f(get)) break;
      true; //never break current
    }
    final def breakAfterNot(f: ⇒ Boolean): Boolean = {
      if (broken || !f) break;
      true; //never break current
    }

    final def breakAfterNot(f: A ⇒ Boolean): Boolean = {
      if (broken || !f(get)) break;
      true; //never break current
    }

    final def stopAfter(f: ⇒ Boolean): Boolean = breakAfter(f);
    final def stopAfter(f: A ⇒ Boolean): Boolean = breakAfter(f);

    /**
     * Frown break
     */
    final def >-:(f: ⇒ Boolean): Boolean = breakAfter(f);

    /**
     * Frown break
     */
    final def >-:(f: A ⇒ Boolean): Boolean = breakAfter(f);

    /**
     * bleeped 4-lettered curse break
     */
    final def *&^:(f: ⇒ Boolean): Boolean = breakAfter(f);

    /**
     * bleeped 4-lettered curse break
     */
    final def *&^:(f: A ⇒ Boolean): Boolean = breakAfter(f);

    final def keepGoing(f: ⇒ Boolean): Boolean = breakAfterNot(f);

    final def keepGoing(f: A ⇒ Boolean): Boolean = breakAfterNot(f);

    final def ><:(f: A => Boolean): Boolean = breakAfter(f);
    /**
     * ******************************************
     * Break when the condition has reached. This leaves the underlying iterator inconsistent
     * *******************************************
     */
    final def breakWhen(f: ⇒ Boolean): Boolean = {
      if (broken || f) { break; return false; }
      else return true;
    }

    final def breakWhen(f: A ⇒ Boolean): Boolean = {
      if (broken || f(get)) { break; return false; }
      else return true;
    }

    final def stopWhen(f: ⇒ Boolean): Boolean = breakWhen(f);

    /**
     * Frown break when
     */
    final def |-:(f: ⇒ Boolean): Boolean = breakWhen(f);

    /**
     * Frown break
     */
    final def |-:(f: A ⇒ Boolean): Boolean = breakWhen(f);

    override def toString: String = get.toString;
    def breakable = this;
  }

  final class BreakableIterator[A](itr: Iterator[A]) extends Iterator[BreakableIteratedValue[A]] {
    private var breakOut = false;
    final class MyBreakableIteratedValue(a: A) extends BreakableIteratedValue[A] {
      @inline override final def get = a;
      @inline override final def break { breakOut = true };
      @inline override final def broken = breakOut;
    }
    @inline final def broken = breakOut;

    @inline final def hasNext: Boolean = !broken && itr.hasNext;
    @inline final def next: BreakableIteratedValue[A] = if (broken) Iterator.empty.next else new MyBreakableIteratedValue(itr.next);

    @inline final def breakable = this;
    @inline final def makeBreakable = this;
    @inline final def allowBreak = this;
    @inline final def allowBreaks = this;
  }

  @inline implicit final def makeBreakableIterator[A](in: Iterator[A]) = new BreakableIterator[A](in);
  @inline implicit final def makeBreakableIterable[A](in: Iterable[A]) = new BreakableIterator[A](in.iterator);

  @inline implicit final def recoverOriginal[A](l: BreakableIteratedValue[A]): A = l.get;
  @inline implicit final def changeRangeToBreakable[X](r: Range) = new BreakableIterator[Int](r.iterator);

}